/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.core.management.processor.apt; import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import org.wildfly.core.management.annotation.XmlName; import org.wildfly.core.management.processor.NameUtils; import org.wildfly.core.management.processor.model.AbstractNamedDescription; import org.wildfly.core.management.processor.model.AttributeGroupDescription; import org.wildfly.core.management.processor.model.NodeClassDescription; import org.wildfly.core.management.processor.model.ResourceDescription; import org.wildfly.core.management.processor.model.RootResourceDescription; import org.wildfly.core.management.processor.model.SchemaDescription; import org.wildfly.core.management.processor.model.SystemDescription; /** * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> */ class SchemaProcessor { static final List<Modifier> EXPECTED_FIELD_MODS = Arrays.asList(Modifier.STATIC, Modifier.FINAL, Modifier.PUBLIC); static final List<Modifier> EXPECTED_METHOD_MODS = Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT); private final ProcessingEnvironment env; private final RoundEnvironment roundEnv; private final MessagerPlus msg; private final SystemDescription.Builder systemDescriptionBuilder; public SchemaProcessor(final ProcessingEnvironment env, final RoundEnvironment roundEnv, final MessagerPlus msg, final SystemDescription.Builder systemDescriptionBuilder) { this.env = env; this.roundEnv = roundEnv; this.msg = msg; this.systemDescriptionBuilder = systemDescriptionBuilder; } public ProcessingEnvironment getEnv() { return env; } public RoundEnvironment getRoundEnv() { return roundEnv; } public void processSchema(final TypeElement schemaAnnotatedElement) { AnnotationMirror retentionAnnotation = null; AnnotationMirror targetAnnotation = null; AnnotationMirror schemaAnnotation = null; AnnotationMirror deprecatedAnnotation = null; AnnotationMirror documentedAnnotation = null; AnnotationMirror inheritedAnnotation = null; for (AnnotationMirror annotationMirror : schemaAnnotatedElement.getAnnotationMirrors()) { final TypeElement annotationTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); final String annotationClassName = annotationTypeElement.getQualifiedName().toString(); switch (annotationClassName) { case "org.wildfly.core.management.annotation.Schema": schemaAnnotation = annotationMirror; break; case "java.lang.annotation.Target": targetAnnotation = annotationMirror; break; case "java.lang.annotation.Retention": retentionAnnotation = annotationMirror; break; case "java.lang.annotation.Documented": documentedAnnotation = annotationMirror; break; case "java.lang.annotation.Inherited": inheritedAnnotation = annotationMirror; break; case "java.lang.Deprecated": deprecatedAnnotation = annotationMirror; break; default: { if (annotationClassName.startsWith("org.wildfly.core.management.annotation")) { msg.error(schemaAnnotatedElement, annotationMirror, "Annotation is not allowed on attribute property"); break; } // ignore break; } } } if (inheritedAnnotation != null) { msg.error(schemaAnnotatedElement, inheritedAnnotation, "Schema annotations may not be @Inherited"); } if (documentedAnnotation == null) { msg.reqWarn(schemaAnnotatedElement, "Schema annotations should be @Documented"); } if (retentionAnnotation != null) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : retentionAnnotation.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "value": { if (! (value instanceof VariableElement)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected an enum RetentionPolicy value for value"); break; } RetentionPolicy retentionPolicy = RetentionPolicy.valueOf(((VariableElement)value).getConstantValue().toString()); if (retentionPolicy != RetentionPolicy.SOURCE) { msg.reqWarn(schemaAnnotatedElement, retentionAnnotation, annotationValue, "Schema member should be annotated with a retention policy of SOURCE"); } break; } default: { unknownAnnotationArgument(schemaAnnotatedElement, schemaAnnotation, annotationValue); break; } } } } else { msg.reqWarn(schemaAnnotatedElement, "Schema member should be annotated with a retention policy of SOURCE"); } if (targetAnnotation != null) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : retentionAnnotation.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "value": { if (value instanceof VariableElement) { ElementType elementType; elementType = ElementType.valueOf(((VariableElement) value).getConstantValue().toString()); if (elementType != ElementType.TYPE) { msg.reqWarn(schemaAnnotatedElement, retentionAnnotation, annotationValue, "Schema member should be annotated with a single target of TYPE"); } } else if (value instanceof List) { @SuppressWarnings("unchecked") List<? extends AnnotationValue> values = (List<? extends AnnotationValue>) value; if (values.size() == 1) { ElementType elementType; elementType = ElementType.valueOf(((VariableElement) values.get(0).getValue()).getConstantValue().toString()); if (elementType != ElementType.TYPE) { msg.reqWarn(schemaAnnotatedElement, retentionAnnotation, annotationValue, "Schema member should be annotated with a single target of TYPE"); } } else { msg.reqWarn(schemaAnnotatedElement, retentionAnnotation, annotationValue, "Schema member should be annotated with a single target of TYPE"); } } else { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected an enum TargetType value for value"); break; } if (! (value instanceof VariableElement)) { break; } break; } default: { unknownAnnotationArgument(schemaAnnotatedElement, schemaAnnotation, annotationValue); break; } } } } if (schemaAnnotation != null) { if (schemaAnnotatedElement.getKind() != ElementKind.ANNOTATION_TYPE) { msg.error(schemaAnnotatedElement, schemaAnnotation, "Only an annotation type may be annotated as a Schema"); return; } final SchemaDescription.Builder builder = SchemaDescription.Builder.create(); processSchemaAnnotation(schemaAnnotatedElement, schemaAnnotation, builder); // now, find all elements for this one processAllSchemaMembers(schemaAnnotatedElement, schemaAnnotation, builder); if (! roundEnv.errorRaised()) { systemDescriptionBuilder.addSchema(builder.build()); } return; } // no schema found - weird but not really a problem return; } private void processSchemaAnnotation(TypeElement schemaAnnotatedElement, AnnotationMirror schemaAnnotation, SchemaDescription.Builder builder) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : schemaAnnotation.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "schemaLocation": { if (! (value instanceof String)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected a String value for schemaLocation"); break; } final URI uri; try { uri = new URI(value.toString()); } catch (URISyntaxException e) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Namespace schema location '%s' is not valid: %s", value, e); break; } final String path = uri.getPath(); if (path == null) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Namespace schema location '%s' does not have a path component", value); break; } final String fileName = path.substring(path.lastIndexOf('/') + 1); if (! fileName.endsWith(".xsd")) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Namespace schema location '%s' must specify a file name ending in \".xsd\"", value); break; } builder.setSchemaLocation(uri); builder.setSchemaFileName(fileName); break; } case "version": { if (! (value instanceof String)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected a String value for version"); break; } builder.setVersion(value.toString()); break; } case "kind": { if (! (value instanceof VariableElement)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected an enum Kind value for kind"); break; } builder.setVersion(((VariableElement)value).getConstantValue().toString()); break; } case "namespace": { if (! (value instanceof String)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected a String value for namespace"); break; } builder.setNamespace(value.toString()); break; } case "compatibilityNamespaces": { if (! (value instanceof List)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Expected an array of Strings value for compatibilityNamespaces"); break; } @SuppressWarnings("unchecked") final List<? extends AnnotationValue> list = (List<? extends AnnotationValue>) value; for (int i = 0; i < list.size(); i++) { final AnnotationValue val = list.get(i); final Object value1 = val.getValue(); if (! (value1 instanceof String)) { msg.errorf(schemaAnnotatedElement, schemaAnnotation, annotationValue, "Element %d of compatibilityNamespaces list is not a String", Integer.valueOf(i)); continue; } builder.addCompatNamespace((String) value1); } break; } default: { unknownAnnotationArgument(schemaAnnotatedElement, schemaAnnotation, annotationValue); break; } } } } private void processAllSchemaMembers(final TypeElement schemaAnnotatedElement, final AnnotationMirror schemaAnnotation, final SchemaDescription.Builder schemaBuilder) { Set<? extends Element> schemaElements = roundEnv.getElementsAnnotatedWith(schemaAnnotatedElement); for (Element itemElement : schemaElements) { if (! (itemElement instanceof TypeElement) || itemElement.getKind() != ElementKind.INTERFACE) { msg.errorf(itemElement, "Schema member items must be interfaces"); continue; } TypeElement itemTypeElement = (TypeElement) itemElement; AnnotationMirror rootResourceAnnotation = null; AnnotationMirror xmlNameAnnotation = null; for (AnnotationMirror annotationMirror : itemTypeElement.getAnnotationMirrors()) { final TypeElement annotationTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); final String annotationClassName = annotationTypeElement.getQualifiedName().toString(); switch (annotationClassName) { case "org.wildfly.core.management.annotation.RootResource": rootResourceAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.XmlName": xmlNameAnnotation = annotationMirror; break; default: { if (annotationClassName.startsWith("org.wildfly.core.management.annotation")) { msg.error(schemaAnnotatedElement, annotationMirror, "Annotation is not allowed on attribute property"); break; } // ignore break; } } } if (rootResourceAnnotation != null) { RootResourceDescription.Builder builder = RootResourceDescription.Builder.create(); processRootResource(schemaAnnotatedElement, rootResourceAnnotation, xmlNameAnnotation, builder); if (! roundEnv.errorRaised()) { final RootResourceDescription rootResourceDescription = builder.build(); schemaBuilder.addRootResourceDescription(rootResourceDescription); } } else { msg.errorf(schemaAnnotatedElement, schemaAnnotation, "Element is part of schema %s but is not a valid schema member type", schemaAnnotation); } } } private void processRootResource(final TypeElement schemaAnnotatedElement, final AnnotationMirror rootResourceAnnotation, final AnnotationMirror xmlNameAnnotation, final RootResourceDescription.Builder builder) { processResource(schemaAnnotatedElement, xmlNameAnnotation, builder); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : rootResourceAnnotation.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "type": { if (! (value instanceof String)) { msg.error(schemaAnnotatedElement, rootResourceAnnotation, annotationValue, "Expected String value for type"); break; } // todo not sure... break; } case "name": { if (! (value instanceof String)) { msg.error(schemaAnnotatedElement, rootResourceAnnotation, annotationValue, "Expected String value for name"); break; } // todo not sure... break; } default: { unknownAnnotationArgument(schemaAnnotatedElement, rootResourceAnnotation, annotationValue); break; } } } } private void processResource(final TypeElement schemaAnnotatedElement, final AnnotationMirror xmlNameAnnotation, final ResourceDescription.Builder builder) { processXmlNameAnnotation(schemaAnnotatedElement, xmlNameAnnotation, builder); builder.setNodeClassDescription(getOrCreateNodeClass(schemaAnnotatedElement)); } private void processNodeClass(final TypeElement nodeClassElement, final NodeClassDescription.Builder builder) { processNamedItem(nodeClassElement, builder); builder.setTypeElement(nodeClassElement); final DeclaredType superclassDeclaredType = (DeclaredType) nodeClassElement.getSuperclass(); if (superclassDeclaredType != null) { final TypeElement superclass = (TypeElement) superclassDeclaredType.asElement(); builder.setSuperClass(getOrCreateNodeClass(superclass)); } for (Element element : nodeClassElement.getEnclosedElements()) { if (element instanceof VariableElement) { if (element.getKind() != ElementKind.FIELD) { msg.errorf(element, "Expected field, found element of type %s", element.getKind()); continue; } else if (!element.getModifiers().containsAll(EXPECTED_FIELD_MODS)) { msg.errorf(element, "Expected public static final field, found modifiers %s", element.getModifiers()); continue; } // fields are unused right now continue; } else if (element instanceof ExecutableElement) { if (element.getKind() != ElementKind.METHOD) { msg.errorf(element, "Expected method, found element of type %s", element.getKind()); continue; } else if (! element.getModifiers().containsAll(EXPECTED_METHOD_MODS)) { msg.errorf(element, "Expected public abstract method, found modifiers %s", element.getModifiers()); continue; } processNodeClassElement((ExecutableElement) element, builder); } else if (element instanceof TypeElement) { // unused right now continue; } else { msg.error(element, "Encountered unexpected/unknown element"); continue; } } } private NodeClassDescription getOrCreateNodeClass(final TypeElement nodeClassElement) { NodeClassDescription description = systemDescriptionBuilder.getNodeClass(nodeClassElement); if (description == null) { final NodeClassDescription.Builder builder = NodeClassDescription.Builder.create(); processNodeClass(nodeClassElement, builder); if (roundEnv.errorRaised()) { description = null; } else { description = builder.build(); systemDescriptionBuilder.addNodeClass(description); } } return description; } private void processNodeClassElement(final ExecutableElement element, final NodeClassDescription.Builder builder) { final String elementName = element.getSimpleName().toString(); final String javaName; if (elementName.startsWith("is")) { javaName = elementName.substring(2); } else if (elementName.startsWith("get")) { javaName = elementName.substring(3); } else { msg.errorf(element, "Method name \"%s\" is not valid for a management node interface property (expected getXxx or isXxx)", elementName); return; } // set a default singular name; it may be overridden via a name="" annotation property // this value won't be used except for sub-resources and collections/maps/etc. String singularJavaName = NameUtils.singular(javaName); // analyze return type final TypeMirror returnType = element.getReturnType(); if (returnType instanceof PrimitiveType) { // simple type } else if (returnType instanceof DeclaredType) { // validate generics final DeclaredType declaredType = (DeclaredType) returnType; final List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); final TypeElement typeElement = (TypeElement) declaredType.asElement(); if (typeElement.getTypeParameters().size() != typeArguments.size()) { msg.error(element, "Type argument count must match declared type's type parameter count"); return; } } else if (returnType instanceof ArrayType) { msg.errorf(element, "Array types are not allowed; use a List<%s> instead", ((ArrayType) returnType).getComponentType()); return; } else { msg.errorf(element, "Unsupported management node interface property return type %s", returnType); return; } // examine annotations AnnotationMirror subResourceAnnotation = null; AnnotationMirror attributeGroupAnnotation = null; AnnotationMirror attributeAnnotation = null; AnnotationMirror xmlNameAnnotation = null; AnnotationMirror virtualAnnotation = null; AnnotationMirror defaultAnnotation = null; AnnotationMirror defaultBooleanAnnotation = null; AnnotationMirror defaultIntAnnotation = null; AnnotationMirror defaultLongAnnotation = null; AnnotationMirror xmlRenderAnnotation = null; AnnotationMirror enumeratedAnnotation = null; for (final AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { final TypeElement annotationTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); final String annotationTypeName = annotationTypeElement.getQualifiedName().toString(); switch (annotationTypeName) { // allowed case "org.wildfly.core.management.annotation.SubResource": subResourceAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.AttributeGroup": attributeGroupAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.Attribute": attributeAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.XmlName": xmlNameAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.Virtual": virtualAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.Default": defaultAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.DefaultBoolean": defaultBooleanAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.DefaultInt": defaultIntAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.DefaultLong": defaultLongAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.XmlRender": xmlRenderAnnotation = annotationMirror; break; case "org.wildfly.core.management.annotation.Enumerated": enumeratedAnnotation = annotationMirror; break; // forbidden default: { if (annotationTypeName.startsWith("org.wildfly.core.management.annotation")) { msg.error(element, annotationMirror, "Annotation is not allowed on attribute property"); } break; } } } // report invalid annotation combinations if (subResourceAnnotation != null) { if (attributeGroupAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, attributeGroupAnnotation); } if (attributeAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, attributeAnnotation); } if (virtualAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, virtualAnnotation); } if (defaultAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultAnnotation); } if (defaultBooleanAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultBooleanAnnotation); } if (defaultIntAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultIntAnnotation); } if (defaultLongAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultLongAnnotation); } } else if (attributeGroupAnnotation != null) { if (virtualAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, virtualAnnotation); } if (defaultAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultAnnotation); } if (defaultBooleanAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultBooleanAnnotation); } if (defaultIntAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultIntAnnotation); } if (defaultLongAnnotation != null) { reportInvalidCombination(element, subResourceAnnotation, defaultLongAnnotation); } } // now, decide the right action to take if (subResourceAnnotation != null) { // it's a sub-resource // perform more specific return type validation if (!(returnType instanceof DeclaredType)) { msg.errorf(element, "Unsupported sub-resource type %s", returnType); return; } } else if (attributeGroupAnnotation != null) { // it's an attribute group // perform more specific return type validation if (!(returnType instanceof DeclaredType)) { msg.errorf(element, "Unsupported attribute group type %s", returnType); return; } final TypeElement returnTypeElement = (TypeElement) ((DeclaredType) returnType).asElement(); if (returnTypeElement.getQualifiedName().toString().startsWith("java.")) { // indication that the type is probably invalid msg.error(element, "JDK types are not supported as attribute groups"); return; } if (returnTypeElement.getKind() != ElementKind.INTERFACE) { msg.error(element, "Attribute group types must be interfaces"); return; } AttributeGroupDescription.Builder agBuilder = AttributeGroupDescription.Builder.create(); // start with Java name agBuilder.setJavaName(javaName); agBuilder.setDmrName(NameUtils.xmlify(javaName)); agBuilder.setXmlName(NameUtils.xmlify(javaName)); agBuilder.setNodeClassDescription(getOrCreateNodeClass((TypeElement) ((DeclaredType) returnType).asElement())); final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = attributeGroupAnnotation.getElementValues(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "name": { if (! (value instanceof String)) { msg.errorf(element, attributeGroupAnnotation, annotationValue, "Expected a String value for name"); break; } agBuilder.setDmrName((String) value); break; } case "required": { if (! (value instanceof Boolean)) { msg.errorf(element, attributeGroupAnnotation, annotationValue, "Expected a boolean value for required"); break; } agBuilder.setRequired(((Boolean) value).booleanValue()); break; } case "anonymous": { if (! (value instanceof Boolean)) { msg.errorf(element, attributeGroupAnnotation, annotationValue, "Expected a boolean value for anonymous"); break; } agBuilder.setPrefixAddress(! ((Boolean) value).booleanValue()); break; } default: { unknownAnnotationArgument(element, attributeGroupAnnotation, annotationValue); break; } } } if (xmlNameAnnotation != null) { processXmlNameAnnotation(element, xmlNameAnnotation, agBuilder); } if (! roundEnv.errorRaised()) { builder.addMember(agBuilder.build()); } } else if (attributeAnnotation != null) { } else { msg.error(element, "Superfluous attribute property"); } } private void unknownAnnotationArgument(final Element element, final AnnotationMirror annotationMirror, final AnnotationValue annotationValue) { msg.error(element, annotationMirror, annotationValue, "Unknown annotation argument"); } private void reportInvalidCombination(final Element element, final AnnotationMirror firstAnnotation, final AnnotationMirror invalidAnnotation) { msg.errorf(element, invalidAnnotation, "%s is not allowed with %s", invalidAnnotation, firstAnnotation); } private void processNamedItem(final TypeElement namedItemElement, final AbstractNamedDescription.Builder builder) { String javaName = builder.getJavaName(); if (javaName == null) { javaName = namedItemElement.getSimpleName().toString(); } builder.setJavaName(javaName); String dmrName = builder.getDmrName(); if (dmrName == null) { dmrName = NameUtils.xmlify(javaName); } builder.setDmrName(dmrName); getXmlName(namedItemElement, builder); } private void processXmlNameAnnotation(final Element element, final AnnotationMirror annotationMirror, final AbstractNamedDescription.Builder builder) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "value": { if (! (value instanceof String)) { msg.error(element, annotationMirror, annotationValue, "Expected String value for value"); break; } builder.setXmlName((String) value); break; } default: { msg.error(element, annotationMirror, annotationValue, "Unknown annotation argument"); break; } } } } private void getXmlName(final TypeElement element, final AbstractNamedDescription.Builder builder) { for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { final TypeElement annotationTypeElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); if (annotationTypeElement.getQualifiedName().toString().equals(XmlName.class.getName())) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) { final AnnotationValue annotationValue = entry.getValue(); final Object value = annotationValue.getValue(); switch (entry.getKey().getSimpleName().toString()) { case "value": { if (! (value instanceof String)) { msg.error(element, annotationMirror, annotationValue, "Expected String value for value"); break; } builder.setXmlName((String) value); break; } default: { msg.error(element, annotationMirror, annotationValue, "Unknown annotation argument"); break; } } } } } } }